#include "lookup.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

vector<RoutingTableEntry> lookupTable;


void insertEntry(const RoutingTableEntry entry) {
    for (int i = 0; i < lookupTable.size(); ++i) {
        RoutingTableEntry *e = &lookupTable[i];
        if (e->addr == entry.addr && e->len == entry.len) {
            e->if_index = entry.if_index;
            e->nexthop = entry.nexthop;
            e->metric = entry.metric;
            return;
        }
    }
    lookupTable.push_back(entry);
}

void del(const RoutingTableEntry entry) {
    for (int i = 0; i < lookupTable.size(); ++i) {
        RoutingTableEntry *e = &lookupTable[i];
        if (e->addr == entry.addr && e->len == entry.len) {
            lookupTable.erase(lookupTable.begin() + i);
            return;
        }
    }
}

void update(bool insert, const RoutingTableEntry entry) {
    // printf("insert\n");
    if (insert) {
        insertEntry(entry);
    } else {
        del(entry);
    }
}

bool match(const in6_addr addr, const in6_addr pattern, uint32_t len) {
//    printf("match\n");
//    printf("addr: %s\n", inet6_ntoa(addr));
//    printf("pattern: %s\n", inet6_ntoa(pattern));
    for (int i = 0; i < len; ++i) {
        int idx = i / 8;
        int shift = 7 - (i % 8);
        uint8_t addr_bit = (addr.s6_addr[idx] >> shift) & 1; // bit value at addr[i]
        uint8_t pattern_bit = (pattern.s6_addr[idx] >> shift) & 1; // bit value at pattern[i]
        //  printf("i:%d, addr_bit: %d, pattern_bit: %d\n", i, addr_bit, pattern_bit);
        if (addr_bit != pattern_bit) {
            return false;
        }
    }
    return true;
}

bool prefix_query(const in6_addr addr, in6_addr *nexthop, uint32_t *if_index) {
    int res = -1;
    uint32_t len = 0;
//    printf("addr: %s\n", inet6_ntoa(addr));
//    printf("lookupTable.size(): %zu\n", lookupTable.size());
    for (int i = 0; i < lookupTable.size(); ++i) {
        RoutingTableEntry e = lookupTable[i];
//           printf("e.addr: %s, e.len: %d\n", inet6_ntoa(e.addr), e.len);
        if (e.len >= len && match(addr, e.addr, e.len)) {
            len = e.len;
            res = i;
        }
    }
    if (res == -1) {
        return false;
    }
    *nexthop = lookupTable[res].nexthop;
    *if_index = lookupTable[res].if_index;
    return true;
}

bool exact_query(const in6_addr addr, uint32_t len, RoutingTableEntry *entry) {
    for (int i = 0; i < lookupTable.size(); ++i) {
        RoutingTableEntry *e = &lookupTable[i];
        if (e->addr == addr && e->len == len) {
            entry->len = e->len;
            entry->addr = e->addr;
            entry->if_index = e->if_index;
            entry->metric = e->metric;
            entry->nexthop = e->nexthop;
            return true;
        }
    }
    return false;
}

int mask_to_len(const in6_addr mask) {
    int len = 120;
    for (int i = 15; i > -1; i--) {
        uint8_t u8 = mask.s6_addr[i];
        if (u8) {
            for (int j = 0; j < 8; ++j) {
                if ((u8 >> j) & 1) {
                    return len + (8 - j);
                }
            }
        }
        len -= 8;
    }
    return 0;
}

in6_addr len_to_mask(int len) {
    if (len < 0 || len > 128) {
        return {0};
    }
    in6_addr mask = {0};
    int idx = 0;
    while (len >= 8) {
        mask.s6_addr[idx] = 0xff;
        len -= 8;
        idx++;
    }
    if (len > 0) {
        int x = 0b10000000;
        for (int i = 0; i < len; ++i) {
            mask.s6_addr[idx] += x >> i;
        }
    }
    return mask;
}

void display_route_table() {
    printf("-----display_route_table---------------\n");
    for (int i = 0; i < lookupTable.size(); ++i) {
        printf("lookupTable[%d]: \n", i);
        RoutingTableEntry e = lookupTable[i];
        printf("addr: %s, len: %u\n", inet6_ntoa(e.addr), e.len);
        printf("if_idx: %x, nexthop: %s, metric: %d\n", e.if_index, inet6_ntoa(e.nexthop), e.metric);
    }
    printf("-----display_route_table end----------\n");
}

vector<RoutingTableEntry> get_lookup_table() {
    return lookupTable;
}